<template>
  <div class="card fileUpload">
    <div class="main">
      <el-upload ref="uploadZip" class="upload-demo" :auto-upload="false" drag accept=".zip" :limit="1" :on-change="onChange">
        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
        <div class="el-upload__text">请拖拽或 <em>点击此处上传</em></div>
        <template #tip>
          <div class="el-upload__tip">请上传zip格式</div>
        </template>
      </el-upload>
      <el-progress v-if="progressShow" :text-inside="true" :stroke-width="26" :percentage="totalProgress" />
      <span v-if="speedShow" style="display: block; margin: 10px 0">
        上传速度: {{ speedMBps }} MB/s 预计剩余时间: {{ remainingTimeStr }}
      </span>
      <div class="btn">
        <el-button type="success" @click="uploadEvent">上传</el-button>
        <el-button v-show="btnShow" type="danger">暂停</el-button>
      </div>
      <div class="importFile">
        <el-input v-model="fileUrl" disabled placeholder="请输入zip文件的完整路径"></el-input>
        <span style="display: block; margin: 8px 0">
          提示：路径格式示例：G://appaa//TestUploadFile2DBAPI//TestUploadFile2DBAPI//wwwroot//folder//file.zip
        </span>
        <el-button ref="importFileRef" type="primary" @click="importFile">导入文件</el-button>
      </div>
    </div>

    <el-dialog v-model="uploadVisible" width="300" :show-close="false">
      <div class="progress-item">
        <div class="progress-label">
          <span>文件 MD5 计算</span>
          <span class="md5-percentage">{{ MD5Count }}%</span>
        </div>
        <el-progress :percentage="MD5Progress" />
      </div>
      <div class="progress-item">
        <div class="progress-label">
          <span>分片 Hash 计算</span>
          <span class="chunk-percentage">{{ HashCount }}%</span>
        </div>
        <el-progress :percentage="HashProgress" />
      </div>
      <div class="status-text">{{ uploadTxt }}</div>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import SparkMD5 from "spark-md5";
import { reactive, ref } from "vue";
import useCurrentInstance from "@/utils/useCurrentInstance";

const { proxy } = useCurrentInstance();
// 上传文件详情弹框底部显示文字
const uploadTxt = ref("正在处理...");
// 导入文件按钮实例
const importFileRef = ref(null);
// 上传文件组件实例
const uploadZip = ref(null);
// 上传文件列表
const fileList = ref([]);
// 上传文件详情弹框
const uploadVisible = ref(false);
// 文件地址
const fileUrl = ref("");
// 暂停按钮是否显示
const btnShow = ref(false);
// 进度条是否显示
const progressShow = ref(false);
// 是否暂停
const isPaused = ref(false);
// 上传队列
const uploadQueue = ref([]);
// 总上传进度
const totalProgress = ref(0);
// 已经上传的分片
const completedChunks = ref(0);
// 已经上传的文件大小
const uploadedSize = ref(0);
// 上传速度是否显示
const speedShow = ref(false);
// 上传速度
const speedMBps = ref(0);
// 剩余时间
const remainingTimeStr = ref("--:--");
// 上传文件详情弹框进度条进度
const MD5Progress = ref(0);
const HashProgress = ref(0);
// 上传文件详情弹框百分比
const MD5Count = ref(0);
const HashCount = ref(0);
// 上传文件开始时间
const startTime = ref(null);
// 当前上传的状态
const currentUploadStatus = reactive({
  fileName: "",
  fileMd5: "",
  totalSize: 0,
  totalChunks: 0,
  uploadedChunks: new Set()
});
// 改变文件函数
const onChange = (file: any, filelist: any) => {
  fileList.value = filelist;
};
// 点击上传文件
const uploadEvent = () => {
  if (fileList.value.length === 0) {
    proxy.$message.error("请选择文件");
    return;
  }
  resetUpload();
  UploadFile();
  // uploadZip.value.submit();
};
// 初始化上传
const resetUpload = () => {
  isPaused.value = false;
  uploadQueue.value = [];
  totalProgress.value = 0;
  completedChunks.value = 0;
  uploadedSize.value = 0;
  btnShow.value = true;
  progressShow.value = true;
  uploadVisible.value = true;
  speedShow.value = true;
  startTime.value = new Date();
};
// UploadFile函数
const UploadFile = async () => {
  const file = fileList.value[0];
  // 获取原始文件名
  const fileName = file.name;
  console.log("开始上传文件:", {
    fileName: fileName,
    fileSize: file.size,
    startTime: new Date().toISOString()
  });
  // 重置并显示进度指示器
  MD5Count.value = 0;
  HashCount.value = 0;
  MD5Progress.value = 0;
  HashProgress.value = 0;
  uploadTxt.value = "正在计算文件 MD5...";

  try {
    // 计算整个文件的 MD5
    const fileMd5 = await calculateFileMD5(file);
    console.log("文件 MD5 计算完成:", fileMd5);
    // 检查文件是否可以秒传
    const checkResult = await checkFileExists(fileMd5, fileName);
    if (checkResult.exists) {
      totalProgress.value = 100;
      uploadVisible.value = false;
      fileUrl.value = checkResult.filePath;
      importFileRef.value.ref.click();
      setTimeout(() => {
        progressShow.value = false;
        proxy.$message.success("文件秒传成功！");
      }, 1000);
      return;
    }
    // 准备分片上传
    totalSize = file.size;
    const bufferChunkSize = getOptimalChunkSize(file.size);
    const totalChunksCount = Math.ceil(totalSize / bufferChunkSize);
    // 初始化上传状态
    currentUploadStatus.fileName = fileName;
    currentUploadStatus.fileMd5 = fileMd5;
    currentUploadStatus.totalSize = totalSize;
    currentUploadStatus.totalChunks = totalChunksCount;
    currentUploadStatus.uploadedChunks = new Set();
    currentUploadStatus.startTime = new Date().toISOString();
    // 获取已上传的分片信息
    const uploadStatus = await checkUploadStatus(fileName, fileMd5, totalSize, totalChunksCount);

    // 重置计数器
    completedChunks.value = 0;
    uploadedSize.value = 0;
    uploadQueue.value = [];
    // 处理每个分片
    for (let partNumber = 1; partNumber <= totalChunksCount; partNumber++) {
      const start = (partNumber - 1) * bufferChunkSize;
      const end = partNumber === totalChunksCount ? file.size : Math.min(start + bufferChunkSize, file.size);

      const chunk = file.slice(start, end);
      const chunkSize = end - start;
      // 更新分片计算进度显示
      let chunkProgress = Math.round((partNumber / totalChunksCount) * 100);
      HashCount.value = chunkProgress;
      HashProgress.value = chunkProgress;
      uploadTxt.value = `正在计算分片 Hash (${partNumber}/${totalChunksCount})`;
      // 计算分片的 hash
      const chunkHash = await calculateChunkHash(chunk);
      console.log(`分片 ${partNumber} hash 计算完成:`, {
        partNumber,
        hash: chunkHash,
        size: chunkSize
      });
      // 检查分片是否已上传
      if (uploadStatus?.chunks?.some(c => c.hash === chunkHash && c.isCompleted)) {
        console.log(`分片 ${partNumber} 已存在，跳过上传`);
        completedChunks.value++;
        uploadedSize.value += chunkSize;
        currentUploadStatus.uploadedChunks.add(partNumber);
        continue;
      }
      // 准备分片信息
      let fileChunkInfo = {
        file: chunk,
        fileName: fileName,
        fileMd5: fileMd5, // 文件MD5
        chunkHash: chunkHash, // 分片hash
        start: start,
        end: end,
        partNumber: partNumber,
        chunks: totalChunksCount,
        size: chunkSize,
        total: file.size
      };
      // 构建URL参数
      const url =
        `partNumber=${partNumber}` +
        `&chunks=${totalChunksCount}` +
        `&size=${chunkSize}` +
        `&start=${start}` +
        `&end=${end}` +
        `&total=${file.size}` +
        `&fileName=${encodeURIComponent(fileName)}` +
        `&fileMd5=${fileMd5}` +
        `&chunkHash=${chunkHash}`;

      fileChunkInfo.urlParameter = url;
      uploadQueue.value.push(fileChunkInfo);
      // 输出分片准备信息
      console.log(`分片 ${partNumber} 准备完成:`, {
        start,
        end,
        size: chunkSize,
        hash: chunkHash,
        isLastChunk: partNumber === totalChunksCount
      });
    }
    // 隐藏hash计算的加载指示器
    uploadVisible.value = false;
    // 更新总进度显示
    const progress = (uploadedSize.value / totalSize) * 100;
    totalProgress.value = progress;

    console.log("待上传分片数量:", uploadQueue.value.length);
    // 如果所有分片都已上传，直接完成
    if (uploadQueue.value.length === 0) {
      totalProgress.value = 100;
      setTimeout(() => {
        progressShow.value = false;
        proxy.$message.success("文件上传完成！");
      }, 1000);
      return;
    }
    // 开始上传剩余的分片
    startTime.value = new Date();
    totalChunks = totalChunksCount;
    processQueue();

    console.log("开始上传分片:", {
      totalChunks: totalChunksCount,
      remainingChunks: uploadQueue.value.length,
      startTime: startTime.value.toISOString()
    });
  } catch (error) {
    // 错误处理
    console.error("文件处理失败:", {
      error: error.message,
      fileName: fileName,
      timeStamp: new Date().toISOString()
    });
    uploadVisible.value = false;
    proxy.$message.error("文件处理失败，请重试");
    progressShow.value = false;
    btnShow.value = false;
  }
};
// 导入文件
const importFile = () => {
  const zipPath = fileUrl.value.trim();
  proxy.$vueAxios
    .post(
      `Values/ImportFile/importfile?ZipFilePath=${zipPath}`,
      {
        filePath: zipPath
      },
      { noLoading: true }
    )
    .then((res: any) => {
      if (res.Mark) {
        proxy.$message.success("文件导入成功！");
      } else {
        proxy.$message.error("文件导入失败，请重试");
      }
    });
};
//辅助函数： 获取文件的md5值
const calculateFileMD5 = (file: any) => {
  return new Promise((resolve, reject) => {
    let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice || Blob.prototype.slice;
    let chunkSize = 2097152; // 2MB
    let chunks = Math.ceil(file.size / chunkSize);

    let currentChunk = 0;
    let spark = new SparkMD5.ArrayBuffer();
    let fileReader = new FileReader();
    uploadTxt.value = "正在计算文件 MD5...";

    fileReader.onload = function (e) {
      spark.append(e.target.result);
      currentChunk++;
      // 更新MD5计算进度
      let progress = Math.round((currentChunk / chunks) * 100);
      MD5Count.value = progress;
      MD5Progress.value = progress;
      if (currentChunk < chunks) {
        loadNext();
      } else {
        let md5 = spark.end();
        resolve(md5);
      }
    };
    fileReader.onerror = e => {
      console.log(e);
      uploadVisible.value = false;
      reject(e);
    };
    function loadNext() {
      let start = currentChunk * chunkSize;
      let end = start + chunkSize >= file.size ? file.size : start + chunkSize;
      fileReader.readAsArrayBuffer(blobSlice.call(file.raw, start, end));
    }
    loadNext();
  });
};
//辅助函数： 检查文件是否可以秒传
const checkFileExists = async (fileMd5: any, fileName: any) => {
  try {
    const data = {
      md5: fileMd5,
      fileName: fileName
    };
    const res = await proxy.$vueAxios.post("upload/CheckFileExists", data, { noLoading: true });
    return res;
  } catch (error) {
    console.error("检查文件失败:", error);
    return { exists: false };
  }
};
// 辅助函数：获取最优分片大小
const getOptimalChunkSize = (fileSize: any) => {
  let chunkSize;
  if (fileSize <= 100 * 1024 * 1024) {
    // <= 100MB
    chunkSize = 10 * 1024 * 1024; // 10MB
  } else if (fileSize <= 1024 * 1024 * 1024) {
    // <= 1GB
    chunkSize = 20 * 1024 * 1024; // 20MB
  } else {
    chunkSize = 10 * 1024 * 1024; // 默认10MB
  }
  // 确保最后一个分片不会太小
  const totalChunks = Math.ceil(fileSize / chunkSize);
  const lastChunkSize = fileSize - (totalChunks - 1) * chunkSize;
  if (lastChunkSize < 1024 * 1024) {
    // 如果最后一个分片小于1MB
    chunkSize = Math.ceil(fileSize / (totalChunks - 1));
  }
  return chunkSize;
};
//辅助函数：检查文件上传状态
const checkUploadStatus = async (fileName: any, fileMd5: any, totalSize: any, totalChunks: any) => {
  try {
    const data = {
      fileName: fileName,
      fileMd5: fileMd5,
      totalSize: totalSize,
      totalChunks: totalChunks
    };
    const response = await proxy.$vueAxios.post("upload/GetUploadStatus", data, { onLoading: true });
    if (response && response.chunks) {
      // 更新已上传的分片信息
      currentUploadStatus.uploadedChunks = new Set(
        response.chunks.filter(chunk => chunk.isCompleted).map(chunk => chunk.chunkNumber)
      );
    }

    return response;
  } catch (error) {
    console.error("检查上传状态失败:", error);
    return null;
  }
};
// 辅助函数：计算单个分片的hash
const calculateChunkHash = async (chunk: any) => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = e => {
      const spark = new SparkMD5.ArrayBuffer();
      spark.append(e.target.result);
      const hash = spark.end();
      resolve(hash);
    };
    reader.onerror = reject;
    reader.readAsArrayBuffer(chunk);
  });
};
// 修改processQueue函数以支持断点续传
const processQueue = () => {
  if (isPaused.value || uploadQueue.value.length === 0) return;
  // 获取队列中的下一个分片
  const chunk = uploadQueue.value.shift();
  UploadFileChunk(chunk);
};
// 优化分片上传函数
const UploadFileChunk = (chunk: any) => {
  let data = new FormData();
  data.append("file", chunk.file, chunk.filePartName);
  const updatedUrl = "upload/upload?" + chunk.urlParameter + "&fileName=" + encodeURIComponent(fileName);
  // $.ajax({
  //   url: updatedUrl,
  //   type: "POST",
  //   data: data,
  //   contentType: false,
  //   processData: false,
  //   xhr: function () {
  //     const xhr = new window.XMLHttpRequest();
  //     xhr.upload.addEventListener(
  //       "progress",
  //       function (evt) {
  //         if (evt.lengthComputable) {
  //           const percentComplete = (evt.loaded / evt.total) * 100;
  //           console.log(`分片 ${chunk.partNumber} 上传进度: ${percentComplete.toFixed(2)}%`);
  //         }
  //       },
  //       false
  //     );
  //     return xhr;
  //   },
  //   success: function (response) {
  //     console.log("分片上传成功:", chunk.partNumber);
  //     currentUploadStatus.uploadedChunks.add(chunk.partNumber);
  //     updateProgress(chunk.file.size);

  //     if (!isPaused.value) {
  //       processQueue();
  //     }
  //   },
  //   error: function (xhr, status, error) {
  //     console.error("分片上传失败:", chunk.partNumber, error);
  //     uploadQueue.value.unshift(chunk);
  //     setTimeout(function () {
  //       if (!isPaused.value) {
  //         processQueue();
  //       }
  //     }, 3000); // 3秒后重试
  //   },
  //   success: function (res) {}
  // });
  proxy.$vueAxios.post(updatedUrl, data, { noLoading: true }).then(res => {
    console.log("分片上传成功:", chunk.partNumber);
    currentUploadStatus.uploadedChunks.add(chunk.partNumber);
    updateProgress(chunk.file.size);
    if (!isPaused.value) {
      processQueue();
    }
  });
};
// 修改updateProgress函数以支持更精确的进度显示
const updateProgress = (chunkSize: any) => {
  completedChunks.value++;
  uploadedSize.value += chunkSize;
  const progress = (uploadedSize.value / totalSize) * 100;
  totalProgress.value = Math.round(progress);
  // 只在有实际上传时才更新速度和剩余时间
  if (uploadQueue.value.length > 0) {
    const currentTime = new Date();
    const duration = (currentTime - startTime.value) / 1000;
    const speed = uploadedSize.value / duration;
    const speedMBps2 = (speed / (1024 * 1024)).toFixed(2);

    const remainingSize = totalSize - uploadedSize.value;
    const remainingTime = remainingSize / speed;
    const remainingMinutes = Math.floor(remainingTime / 60);
    const remainingSeconds = Math.floor(remainingTime % 60);
    const remainingTimeStr2 = `${remainingMinutes}:${remainingSeconds.toString().padStart(2, "0")}`;
    speedMBps.value = speedMBps2;
    remainingTimeStr.value = remainingTimeStr2;
  }

  if (completedChunks.value === totalChunks) {
    $("#pauseButton").hide();
    setTimeout(function () {
      $("#progressContainer").hide();
      alert("文件上传完成！");
    }, 1000);
  }
};
</script>

<style scoped lang="scss">
@import "./index.scss";
</style>
